/*
 * $Id: JsonLibHandler.java 666756 2008-06-11 18:11:00Z hermanns $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package restful.struts2.handler;

import java.io.*;
import java.util.*;

import com.google.gson.*;

import net.sf.json.*;

/**
 * Handles JSON content using json-lib
 * 
 * @author Luca Marrocco
 */
public class JsonLibHandler implements ContentTypeHandler {
	private static final Map classMap = new HashMap() {
		{
			put(".*", HashMap.class);
		}
	};

	public String fromObject(Object obj, String resultCode, Writer toStream) {
		if (obj == null) return null;

		try {
			tryWriteUsingJsonlib(obj, toStream);
		} catch (JSONException e) {
			tryWriteUsingGson(obj, toStream);
		}

		return null;
	}

	private void tryWriteUsingGson(Object obj, Writer toStream) {
		String json = new Gson().toJson(obj);
		write(toStream, json);
	}

	private void tryWriteUsingJsonlib(Object obj, Writer toStream) {
		write(toStream, jsonObjectOrArray(obj));
	}

	private Object jsonObjectOrArray(Object obj) {
		if (isArray(obj)) {
			return JSONArray.fromObject(obj, new JsonConfig());
		}
		return JSONObject.fromObject(obj, new JsonConfig());
	}

	public String getContentType() {
		return "text/javascript";
	}

	public String getExtension() {
		return "json";
	}

	private boolean isArray(Object obj) {
		return obj instanceof Collection || obj.getClass().isArray();
	}

	private boolean isArray(String input) {
		return input.charAt(0) == '[';
	}

	private boolean isntArray(Object object) {
		return !isArray(object);
	}

	private String string(Reader in) {
		try {
			StringBuilder stringBuilder = new StringBuilder();
			char[] buffer = new char[1024];
			int len = 0;
			while ((len = in.read(buffer)) > 0) {
				stringBuilder.append(buffer, 0, len);
			}
			return stringBuilder.toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private Object toArray(Object target, String input) {
		JSONArray jsonArray = JSONArray.fromObject(input);
		boolean classArray = target.getClass().isArray();
		boolean classList = !classArray;
		if (classArray) JSONArray.toArray(jsonArray, target, new JsonConfig());
		if (classList) JSONArray.toList(jsonArray, target, new JsonConfig());
		return target;
	}

	private Object toBean(Object target, String input) {
		JSONObject jsonObject = JSONObject.fromObject(input);
		JSONObject.toBean(jsonObject, target, new JsonConfig());
		return target;
	}

	public Object toObject(Object target, String input) {
		if (target == null) return target;
		if (input.trim().equals("")) return target;
		if (isArray(input)) return toArray(target, input);
		return toBean(target, input);
	}

	public Object toObject(Reader in, Object target) {
		return toObject(target, string(in));
	}

	public Object toObject(String in, Object target) {
		return toObject(target, in);
	}

	private void write(Writer toStream, Object json) {
		try {
			toStream.write(json.toString());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
